package com.ruoyi.web.controller.bikeTwo;

import com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysOrder;
import com.ruoyi.common.dto.BikeDTO;
import com.ruoyi.common.dto.BikeDTOB;
import com.ruoyi.system.service.ISysOrderService;
import com.ruoyi.web.controller.openfeignTwo.FileClientTwo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/api")
public class BikeControllerTwo {
    @Autowired
    private ISysOrderService sysOrderService;
    @Autowired
    private FileClientTwo fileClientTwo;

    @Value("${file.base-http-url-two}")
    private String baseHttpUrl;

    private ThreadPoolExecutor executorService;

    @PostConstruct
    public void init() {
        executorService = new ThreadPoolExecutor(
                10,
                20,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    @PreDestroy
    public void destroy() {
        executorService.shutdown();
    }

/*
    @PostMapping("/power/curve")
    public AjaxResult writeCsv(@RequestBody List<BikeDTO> requestDataList) {
        if (requestDataList == null || requestDataList.isEmpty()) {
            log.error("请求数据为空，无法生成 CSV 文件");
            return AjaxResult.error(400, "请求数据为空，无法生成 CSV 文件");
        }

        int batchSize = 20;
        List<List<BikeDTO>> batches = partitionList(requestDataList, batchSize);
        List<HashMap<String, Object>> finalResultList = Collections.synchronizedList(new ArrayList<>());

        for (List<BikeDTO> batch : batches) {
            String timestamp = String.valueOf(System.currentTimeMillis());
            String randomNum = generateRandomNumber(10);
            String fileName = timestamp + "_" + randomNum + ".csv";
            String filePath = "/data2/" + fileName;

            CompletableFuture<Void> csvFuture = CompletableFuture.runAsync(() -> {
                try (BufferedWriter fileWriter = new BufferedWriter(new FileWriter(filePath))) {
                    fileWriter.write("charging_station_id charging_powers_str");
                    fileWriter.newLine();

                    for (BikeDTO requestData : batch) {
                        List<Integer> powerList = requestData.getPowerList();
                        if (powerList.isEmpty()) {
                            continue;
                        }

                        StringBuilder rowBuilder = new StringBuilder();
                        rowBuilder.append(requestData.getRecordNo()).append(" ").append(powerList.get(0));
                        for (int i = 1; i < powerList.size(); i++) {
                            rowBuilder.append(",").append(powerList.get(i));
                        }

                        synchronized (BikeControllerTwo.class) {
                            fileWriter.write(rowBuilder.toString());
                            fileWriter.newLine();
                        }
                    }
                } catch (IOException e) {
                    log.error("生成 CSV 文件时发生 IO 错误", e);
                }
            }, executorService);

            csvFuture.join();

            Map<String, String> request = new HashMap<>();
            request.put("file_url", baseHttpUrl + fileName);
            Map<String, Object> fileInfo = fileClientTwo.getFile(request);
            if (fileInfo == null || !fileInfo.containsKey("result_file_url")) {
                log.error("文件信息为空，无法获取预测结果文件");
                return AjaxResult.error(500, "无法获取预测结果文件");
            }

            String localFilePath = "/data2/二分类结果.csv";
            List<List<String>> csvData = readCsvFile(localFilePath);
            List<HashMap<String, Object>> resultList = Collections.synchronizedList(new ArrayList<>());

            csvData.stream().skip(1).parallel().forEach(row -> {
                String rawRecordNo = row.get(0);
                String rawResult = row.get(row.size() - 1);

                try {
                    String recordNo = rawRecordNo.split(" ")[0];
                    String result = rawResult.split(" ")[rawResult.split(" ").length - 1];

                    HashMap<String, Object> resultData = new HashMap<>();
                    resultData.put("recordNo", recordNo);
                    resultData.put("result", Integer.parseInt(result));
                    resultList.add(resultData);
                } catch (NumberFormatException e) {
                    log.error("解析文件时发生错误", e);
                }
            });

            finalResultList.addAll(resultList);

            // 删除生成的 CSV 文件
            try {
                Files.deleteIfExists(Paths.get(filePath));
                log.info("成功删除文件: {}", filePath);
            } catch (IOException e) {
                log.error("删除文件失败: {}", filePath, e);
            }
        }

        return AjaxResult.success(finalResultList);
    }
*/

    private List<List<BikeDTO>> partitionList(List<BikeDTO> list, int batchSize) {
        List<List<BikeDTO>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            int end = Math.min(i + batchSize, list.size());
            batches.add(list.subList(i, end));
        }
        return batches;
    }

    private List<List<String>> readCsvFile(String filePath) {
        List<List<String>> records = new ArrayList<>();
        try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath));
             CSVReader csvReader = new CSVReader(reader)) {
            records = csvReader.readAll().stream().map(Arrays::asList).collect(Collectors.toList());
        } catch (IOException | CsvException e) {
            log.error("读取 CSV 文件失败，路径：{}", filePath, e);
        }
        return records;
    }

    private String generateRandomNumber(int length) {
        Random random = new Random();
        return random.ints(0, 10).limit(length).mapToObj(String::valueOf).collect(Collectors.joining());
    }






//    @PostMapping("/predict")
//    public AjaxResult predict(@RequestBody List<BikeDTO> requestDataList) {
//
//
//
//
//
//        // 转换为 BikeDTOB（powerList 是 String）
//        List<BikeDTOB> convertedList = new ArrayList<>();
//
//        for (BikeDTO dto : requestDataList) {
//
//
//            SysOrder sysOrder = new SysOrder();
//            sysOrder.setRecordNo(dto.getRecordNo());
//            sysOrder.setPowerList(dto.getPowerList().toString());
//            sysOrder.setCreateTime(new Date());
//            sysOrder.setMarkType(dto.getMarkType());
//            sysOrder.setDeleteTime(0);
//            try {
//                sysOrderService.save(sysOrder);
//            } catch (Exception e) {
//                throw new RuntimeException(e);
//            }
//
//
//            BikeDTOB dtoB = new BikeDTOB();
//            dtoB.setRecordNo(dto.getRecordNo());
//
//            // List<Integer> 转逗号拼接字符串
//            String powerListStr = dto.getPowerList().stream()
//                    .map(String::valueOf)
//                    .collect(Collectors.joining(","));
//
//            dtoB.setPowerList(powerListStr);
//            convertedList.add(dtoB);
//        }
//
//        // 调用 Feign 接口（注意参数类型是 List<BikeDTOB>）
//        Map<String, Object> fileInfo = fileClientTwo.getFile(convertedList);
//
//
//        // 获取返回的数据中的“data”部分
//        List<Map<String, Object>> resultList = (List<Map<String, Object>>) fileInfo.get("data");
//
//        // 提取需要的字段（recordNo 和 result）
//        List<Map<String, Object>> responseList = resultList.stream()
//                .map(item -> {
//                    Map<String, Object> resultMap = new HashMap<>();
//                    resultMap.put("recordNo", item.get("recordNo"));
//                    resultMap.put("result", item.get("result"));
//                    return resultMap;
//                })
//                .collect(Collectors.toList());
//
//        // 返回响应结果
//        return AjaxResult.success(responseList);
//    }
//


//4月23日    @PostMapping("bike/order/process")
//    @Transactional
//    public AjaxResult predict(@RequestBody List<BikeDTO> requestDataList) {
//        // 转换为 BikeDTOB（powerList 是 String）
//        List<BikeDTOB> convertedList = new ArrayList<>();
//
//        // 结果列表
//        List<Map<String, Object>> responseList = new ArrayList<>();
//
//        for (BikeDTO dto : requestDataList) {
//            // 1. 保存新订单到数据库
//            SysOrder sysOrder = new SysOrder();
//            sysOrder.setRecordNo(dto.getRecordNo());
//            sysOrder.setPowerList(dto.getPowerList().toString());
//            sysOrder.setCreateTime(new Date());
//            sysOrder.setMarkType(dto.getMarkType());
//            sysOrder.setDeleteTime(0);
//
//            try {
//                sysOrderService.save(sysOrder);  // 将新订单保存到数据库
//            } catch (Exception e) {
//                throw new RuntimeException("Error while saving SysOrder: " + e.getMessage());
//            }
//
//            // 2. 创建 BikeDTOB 用于调用 Feign 接口
//            BikeDTOB dtoB = new BikeDTOB();
//            dtoB.setRecordNo(dto.getRecordNo());
//
//            // List<Integer> 转逗号拼接字符串
//            String powerListStr = dto.getPowerList().stream()
//                    .map(String::valueOf)
//                    .collect(Collectors.joining(","));
//            dtoB.setPowerList(powerListStr);
//
//            // 将 BikeDTOB 添加到 List
//            convertedList.add(dtoB);
//        }
//
//        // 3. 调用 Feign 接口（注意参数类型是 List<BikeDTOB>）
//        Map<String, Object> fileInfo = fileClientTwo.getFile(convertedList);
//
//        // 4. 获取返回的数据中的“data”部分
//        List<Map<String, Object>> resultList = (List<Map<String, Object>>) fileInfo.get("data");
//
//        // 5. 提取需要的字段（recordNo 和 result）并更新数据库中的记录
//        for (Map<String, Object> item : resultList) {
//            Map<String, Object> resultMap = new HashMap<>();
//            String recordNo = (String) item.get("recordNo");
//            Object result = item.get("result");
//
//            // 提取 result 和 recordNo
//            resultMap.put("recordNo", recordNo);
//            resultMap.put("result", result);
//
//
//                // 6. 使用 MyBatis-Plus 的 LambdaUpdateWrapper 更新数据库中的 result 字段
//            try {
//                boolean update = sysOrderService.update(
//                        new SysOrder(),
//                        new LambdaUpdateWrapper<SysOrder>()
//                                .eq(SysOrder::getRecordNo, recordNo)  // 匹配 recordNo
//                                .set(SysOrder::getResult, result != null ? Long.valueOf(result.toString()) : null)  // 设置 result 字段
//                );
//            } catch (NumberFormatException e) {
//                throw new RuntimeException("更新数据库异常："+e);
//            }
//
//
//            // 将每条记录添加到响应列表
//            responseList.add(resultMap);
//        }
//
//        // 8. 返回响应结果
//        return AjaxResult.success(responseList);
//    }
//

//
//    @PostMapping("bike/order/process")
//    @Transactional
//    public AjaxResult predict(@RequestBody List<BikeDTO> requestDataList) {
//        List<BikeDTOB> convertedList = new ArrayList<>();
//        List<SysOrder> orderList = new ArrayList<>();
//        List<Map<String, Object>> responseList = new ArrayList<>();
//
//        // 1. 批量构建 SysOrder 和 BikeDTOB 列表
//        for (BikeDTO dto : requestDataList) {
//            // 构建订单对象
//            SysOrder sysOrder = new SysOrder();
//            sysOrder.setRecordNo(dto.getRecordNo());
//            sysOrder.setPowerList(dto.getPowerList().toString());
//            sysOrder.setCreateTime(new Date());
//            sysOrder.setMarkType(dto.getMarkType());
//            sysOrder.setDeleteTime(0);
//            orderList.add(sysOrder);
//
//            // 构建 DTOB 对象
//            BikeDTOB dtoB = new BikeDTOB();
//            dtoB.setRecordNo(dto.getRecordNo());
//            String powerListStr = dto.getPowerList().stream()
//                    .map(String::valueOf)
//                    .collect(Collectors.joining(","));
//            dtoB.setPowerList(powerListStr);
//            convertedList.add(dtoB);
//        }
//
//        // 2. 批量保存订单
//        try {
//            sysOrderService.saveBatch(orderList);
//        } catch (Exception e) {
//            throw new RuntimeException("批量保存订单失败: " + e.getMessage());
//        }
//
//        // 3. 调用 Feign 接口
//        Map<String, Object> fileInfo = fileClientTwo.getFile(convertedList);
//        List<Map<String, Object>> resultList = (List<Map<String, Object>>) fileInfo.get("data");
//
//        // 4. 构建批量更新数据
//        Map<String, Long> recordResultMap = new HashMap<>();
//        for (Map<String, Object> item : resultList) {
//            String recordNo = (String) item.get("recordNo");
//            Object result = item.get("result");
//
//            Long resultValue = null;
//            try {
//                if (result != null) {
//                    resultValue = Long.valueOf(result.toString());
//                }
//            } catch (NumberFormatException e) {
//                throw new RuntimeException("转换 result 出错: " + result);
//            }
//
//            recordResultMap.put(recordNo, resultValue);
//
//            // 添加到响应列表
//            Map<String, Object> resultMap = new HashMap<>();
//            resultMap.put("recordNo", recordNo);
//            resultMap.put("result", resultValue);
//            responseList.add(resultMap);
//        }
//
//        // 5. 批量更新数据库中的 result 字段
//        if (!recordResultMap.isEmpty()) {
//            sysOrderService.updateBatchByRecordNos(recordResultMap);
//        }
//
//        return AjaxResult.success(responseList);
//    }
//


    /**
     * 添加了死锁重试机制
     * @param requestDataList
     * @return
     */
/*    @PostMapping("bike/order/process")
    @Transactional 钱老师可以用的
    public AjaxResult predict(@RequestBody List<BikeDTO> requestDataList) {
        List<BikeDTOB> convertedList = new ArrayList<>();
        List<SysOrder> orderList = new ArrayList<>();
        List<Map<String, Object>> responseList = new ArrayList<>();

        // 1. 批量构建 SysOrder 和 BikeDTOB 列表
        for (BikeDTO dto : requestDataList) {
            // 构建订单对象
            SysOrder sysOrder = new SysOrder();
            sysOrder.setRecordNo(dto.getRecordNo());
            sysOrder.setPowerList(dto.getPowerList().toString());
            sysOrder.setCreateTime(new Date());
            sysOrder.setMarkType(dto.getMarkType());
            sysOrder.setDeleteTime(0);
            sysOrder.setVersion(0);
            orderList.add(sysOrder);

            // 构建 DTOB 对象
            BikeDTOB dtoB = new BikeDTOB();
            dtoB.setRecordNo(dto.getRecordNo());
            String powerListStr = dto.getPowerList().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            dtoB.setPowerList(powerListStr);
            convertedList.add(dtoB);
        }

        // 2. 批量保存订单
        try {
            sysOrderService.saveBatch(orderList);
        } catch (Exception e) {
            throw new RuntimeException("批量保存订单失败: " + e.getMessage());
        }

        // 3. 调用 Feign 接口
        Map<String, Object> fileInfo = fileClientTwo.getFile(convertedList);

        List<Map<String, Object>> resultList = (List<Map<String, Object>>) fileInfo.get("data");

        // 4. 构建批量更新数据
        Map<String, Long> recordResultMap = new HashMap<>();
        for (Map<String, Object> item : resultList) {
            String recordNo = (String) item.get("recordNo");
            Object result = item.get("result");

            Long resultValue = null;
            try {
                if (result != null) {
                    resultValue = Long.valueOf(result.toString());
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("转换 result 出错: " + result);
            }

            recordResultMap.put(recordNo, resultValue);

            // 添加到响应列表
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("recordNo", recordNo);
            resultMap.put("result", resultValue);
            responseList.add(resultMap);
        }

        // 5. 批量更新数据库中的 result 字段（带重试机制）
        if (!recordResultMap.isEmpty()) {
            int retryCount = 3; // 重试次数
            long delayBetweenRetries = 2000L; // 每次重试的等待时间（单位：毫秒）
            boolean success = false;

            for (int attempt = 1; attempt <= retryCount; attempt++) {
                try {
                    sysOrderService.updateBatchByRecordNos(recordResultMap);
                    success = true;
                    break; // 如果更新成功，退出重试循环
                } catch (Exception e) {
                    // 检查是否为死锁异常
                    if (e.getCause() instanceof MySQLTransactionRollbackException) {
                        // 检查是否为死锁错误，错误码通常为 1213
                        if (e.getMessage().contains("Deadlock found when trying to get lock")) {
                            // 如果是死锁错误，记录日志并进行重试
                            log.error("第 {} 次重试：检测到死锁，正在重试...", attempt);
                            if (attempt < retryCount) {
                                try {
                                    TimeUnit.MILLISECONDS.sleep(delayBetweenRetries); // 等待一段时间后重试
                                } catch (InterruptedException ie) {
                                    Thread.currentThread().interrupt(); // 恢复中断状态
                                }
                            } else {
                                throw new RuntimeException("批量更新数据库失败，重试已达到最大次数: " + e.getMessage());
                            }
                        } else {
                            throw e; // 如果不是死锁异常，直接抛出异常
                        }
                    } else {
                        throw e; // 如果不是死锁异常，直接抛出异常
                    }
                }
            }

            if (!success) {
                throw new RuntimeException("批量更新数据库失败，重试已达到最大次数");
            }
        }

        return AjaxResult.success(responseList);
    }*/

/*--------------------------------------------添加批量处理------------------------------------------------------------*/


    @PostMapping("bike/order/process")
    @Transactional
    public AjaxResult predict(@RequestBody List<BikeDTO> requestDataList) {
        // 模拟数据：如果未传入任何数据，则构造一万条
/*        if (requestDataList == null || requestDataList.isEmpty()) {
            requestDataList = generateTestData(10000);
        }*/



        List<BikeDTOB> convertedList = new ArrayList<>();
        List<SysOrder> orderList = new ArrayList<>();
        List<Map<String, Object>> responseList = new ArrayList<>();

        // 1. 构建订单与 DTOB
        for (BikeDTO dto : requestDataList) {
            SysOrder sysOrder = new SysOrder();
            sysOrder.setRecordNo(dto.getRecordNo());
            sysOrder.setPowerList(dto.getPowerList().toString());
            sysOrder.setCreateTime(new Date());
            sysOrder.setMarkType(dto.getMarkType());
            sysOrder.setDeleteTime(0);
            sysOrder.setVersion(0);
            orderList.add(sysOrder);

            BikeDTOB dtoB = new BikeDTOB();
            dtoB.setRecordNo(dto.getRecordNo());
            String powerListStr = dto.getPowerList().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            dtoB.setPowerList(powerListStr);
            convertedList.add(dtoB);
        }

        // 2. 批量保存订单
        try {
            sysOrderService.saveBatch(orderList);
        } catch (Exception e) {
            throw new RuntimeException("批量保存订单失败: " + e.getMessage());
        }

        // 3. 分批调用 OpenFeign 接口，每批最多 1000 条
        int batchSize = 1000;
        List<Map<String, Object>> allResults = new ArrayList<>();
        for (int i = 0; i < convertedList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, convertedList.size());
            List<BikeDTOB> batch = convertedList.subList(i, end);
            Map<String, Object> fileInfo = fileClientTwo.getFile(batch);
            List<Map<String, Object>> batchResult = (List<Map<String, Object>>) fileInfo.get("data");
            if (batchResult != null) {
                allResults.addAll(batchResult);
            }
        }

        // 4. 构建更新数据
        Map<String, Long> recordResultMap = new HashMap<>();
        for (Map<String, Object> item : allResults) {
            String recordNo = (String) item.get("recordNo");
            Object result = item.get("result");
            Long resultValue = null;
            try {
                if (result != null) {
                    resultValue = Long.valueOf(result.toString());
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("转换 result 出错: " + result);
            }
            recordResultMap.put(recordNo, resultValue);

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("recordNo", recordNo);
            resultMap.put("result", resultValue);
            responseList.add(resultMap);
        }

        // 5. 批量更新数据库中的 result 字段（带重试机制）
        if (!recordResultMap.isEmpty()) {
            int retryCount = 3;
            long delayBetweenRetries = 2000L;
            boolean success = false;

            for (int attempt = 1; attempt <= retryCount; attempt++) {
                try {
                    sysOrderService.updateBatchByRecordNos(recordResultMap);
                    success = true;
                    break;
                } catch (Exception e) {
                    if (e.getCause() instanceof MySQLTransactionRollbackException &&
                            e.getMessage().contains("Deadlock found when trying to get lock")) {
                        log.error("第 {} 次重试：检测到死锁，正在重试...", attempt);
                        if (attempt < retryCount) {
                            try {
                                TimeUnit.MILLISECONDS.sleep(delayBetweenRetries);
                            } catch (InterruptedException ie) {
                                Thread.currentThread().interrupt();
                            }
                        } else {
                            throw new RuntimeException("批量更新数据库失败，重试已达到最大次数: " + e.getMessage());
                        }
                    } else {
                        throw e;
                    }
                }
            }

            if (!success) {
                throw new RuntimeException("批量更新数据库失败，重试已达到最大次数");
            }
        }

        return AjaxResult.success(responseList);
    }

    private List<BikeDTO> generateTestData(int count) {
        List<BikeDTO> dataList = new ArrayList<>();
        Random random = new Random();
        List<Integer> samplePowerList = Arrays.asList(288, 289, 290, 291, 292, 293, 294, 295);

        for (int i = 0; i < count; i++) {
            BikeDTO dto = new BikeDTO();
            dto.setRecordNo("RECORD_NO_" + String.format("%05d", i));

            // 模拟 powerList：每条随机取 10~20 个元素
            List<Integer> powerList = new ArrayList<>();
            int size = 10 + random.nextInt(11); // 10~20
            for (int j = 0; j < size; j++) {
                powerList.add(samplePowerList.get(random.nextInt(samplePowerList.size())));
            }
            dto.setPowerList(powerList);

            dataList.add(dto);
        }
        return dataList;
    }

}
